সি ফাইল (File Handling) এবং স্ট্রাকচার (Structure) দুটি গুরুত্বপূর্ণ ধারণা যা সি প্রোগ্রামিং ভাষায় ডেটা প্রক্রিয়া এবং সংরক্ষণ ব্যবস্থার উন্নতি করতে সাহায্য করে।
সি প্রোগ্রামে ফাইল হ্যান্ডলিং একটি গুরুত্বপূর্ণ বিষয়। ফাইলের মাধ্যমে ডেটা সংরক্ষণ, পড়া এবং লেখার কাজ করা হয়। সি ভাষায় ফাইল হ্যান্ডলিং করার জন্য stdio.h
হেডার ফাইল ব্যবহৃত হয়, যেখানে ফাইল সংক্রান্ত বিভিন্ন ফাংশনগুলো (যেমন fopen()
, fclose()
, fread()
, fwrite()
, fprintf()
, fscanf()
, ইত্যাদি) রয়েছে।
fopen()
ফাংশনের মাধ্যমে একটি ফাইল খোলা হয়। ফাইল খোলার সময় একটি মোড (যেমন r
, w
, a
, ইত্যাদি) নির্ধারণ করতে হয়।fprintf()
, fputs()
, অথবা fwrite()
ফাংশন ব্যবহার করা হয়।fscanf()
, fgets()
, অথবা fread()
ফাংশন ব্যবহার করা হয়।fclose()
ফাংশন ব্যবহার করে ফাইলটি বন্ধ করা হয়।#include <stdio.h>
int main() {
FILE *fptr;
char str[100];
// ফাইল খোলা লেখার জন্য
fptr = fopen("example.txt", "w");
if (fptr == NULL) {
printf("Error opening file!\n");
return 1;
}
// ফাইলের মধ্যে লেখা
fprintf(fptr, "Hello, World!\n");
fprintf(fptr, "This is a C file handling example.\n");
// ফাইল বন্ধ করা
fclose(fptr);
// ফাইল খোলা পড়ার জন্য
fptr = fopen("example.txt", "r");
if (fptr == NULL) {
printf("Error opening file!\n");
return 1;
}
// ফাইল থেকে একটি লাইন পড়া
while (fgets(str, sizeof(str), fptr) != NULL) {
printf("%s", str);
}
// ফাইল বন্ধ করা
fclose(fptr);
return 0;
}
ব্যাখ্যা:
fopen("example.txt", "w")
: এই লাইনে ফাইল example.txt
লেখার জন্য খোলা হয়।fprintf(fptr, ...)
: এটি ফাইলের মধ্যে ডেটা লেখে।fgets(str, sizeof(str), fptr)
: এটি ফাইল থেকে একটি লাইন পড়ে এবং স্ক্রীনে প্রিন্ট করে।fclose(fptr)
: ফাইল বন্ধ করা হয়।স্ট্রাকচার (Structure) হল একটি কাস্টম ডেটা টাইপ, যা একাধিক ভিন্ন ডেটা টাইপের সদস্য ধারণ করতে সক্ষম। এটি ব্যবহারকারীর জন্য নিজের কাস্টম ডেটা টাইপ তৈরি করার একটি উপায়। স্ট্রাকচারের মাধ্যমে বিভিন্ন ধরনের ডেটা একত্রিত করা হয় এবং একসাথে পরিচালনা করা হয়।
int
, float
, char
, ইত্যাদি।#include <stdio.h>
// স্ট্রাকচার ডিফিনেশন
struct Person {
char name[50];
int age;
float height;
};
int main() {
// স্ট্রাকচার ভেরিয়েবল
struct Person person1;
// সদস্যগুলির মান ইনিশিয়ালাইজ করা
printf("Enter name: ");
scanf("%s", person1.name);
printf("Enter age: ");
scanf("%d", &person1.age);
printf("Enter height: ");
scanf("%f", &person1.height);
// স্ট্রাকচারের সদস্যগুলো প্রিন্ট করা
printf("\nPerson Details:\n");
printf("Name: %s\n", person1.name);
printf("Age: %d\n", person1.age);
printf("Height: %.2f\n", person1.height);
return 0;
}
ব্যাখ্যা:
struct Person { ... }
: এখানে Person
নামক একটি স্ট্রাকচার ডিফাইন করা হয়েছে যা name
, age
, এবং height
নামে তিনটি সদস্য ধারণ করে।person1
: Person
স্ট্রাকচারের একটি ভেরিয়েবল, যা এই স্ট্রাকচারের সদস্যগুলির মান ধারণ করে।স্ট্রাকচার পয়েন্টারের মাধ্যমে স্ট্রাকচারের সদস্যগুলিতে অ্যাক্সেস করা যায়।
#include <stdio.h>
struct Person {
char name[50];
int age;
};
int main() {
struct Person person1 = {"John", 25};
struct Person *ptr = &person1;
// পয়েন্টার ব্যবহার করে স্ট্রাকচারের সদস্য অ্যাক্সেস
printf("Name: %s\n", ptr->name);
printf("Age: %d\n", ptr->age);
return 0;
}
ব্যাখ্যা:
ptr->name
: পয়েন্টার ব্যবহার করে স্ট্রাকচারের সদস্য name
এবং age
অ্যাক্সেস করা হয়।&person1
: এটি person1
স্ট্রাকচারের মেমরি ঠিকানা নির্দেশ করে।ফাইল এবং স্ট্রাকচার একসাথে ব্যবহার করে একটি ফাইলের মধ্যে স্ট্রাকচারের ডেটা সংরক্ষণ করা যায়। উদাহরণস্বরূপ, আমরা স্টুডেন্ট তথ্য একটি স্ট্রাকচারে ধারণ করতে পারি এবং সেটি একটি ফাইলে সংরক্ষণ করতে পারি।
#include <stdio.h>
struct Student {
int id;
char name[50];
float grade;
};
int main() {
struct Student student1 = {1, "Alice", 88.5};
// ফাইল খোলা লেখার জন্য
FILE *fptr = fopen("student_data.dat", "wb");
if (fptr == NULL) {
printf("Error opening file.\n");
return 1;
}
// স্ট্রাকচারটি ফাইলে লেখা
fwrite(&student1, sizeof(struct Student), 1, fptr);
// ফাইল বন্ধ করা
fclose(fptr);
// ফাইল থেকে স্ট্রাকচার পড়া
struct Student student2;
fptr = fopen("student_data.dat", "rb");
if (fptr == NULL) {
printf("Error opening file.\n");
return 1;
}
fread(&student2, sizeof(struct Student), 1, fptr);
// স্ট্রাকচারটি প্রিন্ট করা
printf("Student ID: %d\n", student2.id);
printf("Student Name: %s\n", student2.name);
printf("Student Grade: %.2f\n", student2.grade);
fclose(fptr);
return 0;
}
ব্যাখ্যা:
fwrite(&student1, sizeof(struct Student), 1, fptr);
: স্ট্রাকচার student1
কে ফাইলে লেখা হচ্ছে।fread(&student2, sizeof(struct Student), 1, fptr);
: ফাইল থেকে স্ট্রাকচার student2
পড়ে আসছে।fopen()
, fwrite()
, fread()
, fclose()
ফাংশন দিয়ে ফাইলের মধ্যে ডেটা লেখা, পড়া এবং ফাইল বন্ধ করা হয়।এবং স্ট্রাকচার একসাথে ব্যবহার করে আপনি আরও শক্তিশালী ডেটা পরিচালনা ব্যবস্থা তৈরি করতে পারেন।
এই অধ্যায়ে আপনি সি প্রোগ্রামিং স্ট্রাকচার(structures) সম্মন্ধে জানবেন; স্ট্রাকচার কি, স্ট্রাকচার কিভাবে ডিফাইন্ড(defined) করতে হয় এবং কিভাবে ব্যবহার করতে হয়।
সি প্রোগ্রামিং এ স্ট্রাকচার হলো ইউজার ডিফাইন্ড(user defined) ডেটা টাইপ।
একটি একক নামের মধ্যে বিভিন্ন ডেটা টাইপ সম্বলিত ভ্যারিয়েবলের সংগ্রহ-ই হলো স্ট্রাকচার(structures)
উদাহরণস্বরূপঃ আপনি একজন ব্যাক্তির(নাম, আইডি এবং ঠিকানা) তথ্য সংগ্রহ করে রাখতে চাচ্ছেন। ব্যাক্তির তথ্য সংগ্রহ করে রাখার জন্য আপনি খুব সহজেই বিভিন্ন ভ্যারিয়েবল তৈরি করতে পারেন। যেমন- name, idNo, address ইত্যাদি।
যাইহোক, আপনি যদি অনেক ব্যাক্তির তথ্য সংগ্রহ করে রাখতে চান তাহলে আপনাকে প্রত্যেক ব্যাক্তির জন্য name1, idNo1, address1, name2, idNo2, address2 ইত্যাদি ভিন্ন ভিন্ন ভ্যারিয়েবল তৈরি করতে হবে।
আপনি নিশ্চয় অনুমান করতে পারছেন যে, কোড অনেক দীর্ঘ হবে এবং নোংরা দেখাবে। এছাড়া ভ্যারিয়েবল(তথ্য) গুলোর মধ্যে যেহেতু কোনো সম্পর্ক নাই তাই ইহা ভীতিকর কার্যেও পরিণত হতে চলেছে।
আরোও সহজ এবং সঠিক পদ্ধতি হবে যদি সম্পর্কযুক্ত তথ্যসমূহকে একটি একক নাম Person
এর মধ্যে সংগ্রহ করে রাখেন এবং প্রত্যেক ব্যক্তির জন্য ইহা ব্যবহার করেন। এখন কোড দেখতে অনেক স্বচ্ছ ও পাঠযোগ্য হবে এবং কোডের কর্মদক্ষতাও বাড়বে।
একটি একক নাম(Person
) এর মাধ্যমে সম্পর্কযুক্ত তথ্যসমূহের এই সংগ্রহ-ই হলো স্ট্রাকচার(structure)।
struct
কীওয়ার্ডের মাধ্যমে স্ট্রাকচার তৈরি করা হয়।
struct structure_name
{
data_type member1;
data_type member2;
.
.
data_type memeber;
};
নোটঃ লাইনের শেষে সেমিকোলন(;) দিতে ভুল করবেন না।
উপরোল্লিখিত person
এর জন্য আমরা নিম্নের ন্যায় স্ট্রাকচার তৈরি করতে পারিঃ
struct person
{
char name[50];
int idNo;
float address;
};
উপরের ডিক্লেয়ারেশনে(declaration) ডিরাইভড(derived) ডেটা টাইপ struct person
তৈরি হয়েছে।
যখন কোনো স্ট্রাকচার ডিফাইন্ড করা হয় তখন ইহা ইউজার ডিফাইন্ড(user-defined) ডেটা টাইপ তৈরি করে। কিন্তু কোনো মেমোরি বা স্টোরেজ বরাদ্দ হয় না।
উপরের person স্ট্রাকচারের জন্য নিচের ন্যায় ভ্যারিয়েবল ডিক্লেয়ার করা যেতে পারেঃ
struct person
{
char name[50];
int idNo;
float address;
};
int main()
{
struct person person1, person2, person3[30];
return 0;
}
স্ট্রাকচার ভ্যারিয়েবল ডিক্লেয়ারের অন্য পদ্ধতিঃ
struct person
{
char name[30];
int idNo;
float address;
} person1, person2, person3[30];
উভয় ক্ষেত্রেই person1 ও person2 ভ্যারিয়েবল এবং person3 অ্যারের ডেটা টাইপ হলো person
স্ট্রাকচারের মেম্বারকে এক্সেস করার জন্য দুই ধরণের অপারেটর রয়েছেঃ
- Member operator(.)
- Structure pointer operator(->)
স্ট্রাকচারের মেম্বার ভ্যারিয়েবলকে নিচের ন্যায় এক্সেস(access) করা যায়ঃ
structure_variable_name.member_name
ধরুন, আমরা person1 ভ্যারিয়েবলের address কে এক্সেস করতে চাচ্ছি তাহলে ইহা নিচের ন্যায় এক্সেস করা যায়।
person1.address
ইউজার কর্তৃক ভ্যালু ইনপুট নিয়ে দুটি দুরত্বকে যোগ করার জন্য সি প্রোগ্রাম। দুরত্ব পরিমাপ করার জন্য ইঞ্চি এবং ফুট ব্যবহার করুন। (বিঃদ্রঃ ১২ ইঞ্চি = ১ ফুট)
kt_satt_skill_example_id=505
স্ট্রাকচার ভ্যারিয়েবল ডিক্লেয়ার করার জন্য struct structure_name variable_name;
ব্যবহার করলে ডেভেলপমেন্টের জন্য সময় বেশী লাগে। ফলে struct
ব্যবহারের তাৎপর্য থেকে বঞ্চিত হতে হয়।
সুতরাং স্ট্রাকচারের নাম দেওয়ার জন্য ডেভেলপাররা সাধারণত typedef
কীওয়ার্ড ব্যবহার করে। উদাহরণস্বরূপঃ
typedef struct complex
{
int imag;
float real;
} comp;
int main()
{
comp comp1, comp2;
}
এখানে typedef
কীওয়ার্ড ব্যবহার করে comp টাইপ তৈরি করা হয়েছে যেখানে com এর টাইপ হলো struct complex
)।
তারপরে comp টাইপের comp1 এবং comp2 দুটি স্ট্রাকচার ভ্যারিয়েবল তৈরি করা হয়েছে।
সি প্রোগ্রামিং এ একটি Structure অন্য structure এর মধ্যে নেস্টেড হতে পারে।
struct complex
{
int imaginary_value;
float real_value;
};
struct number
{
struct complex comp;
int real;
} number1, number2;
ধরুন, আপনি number2 স্ট্রাকচার ভ্যারিয়েবলের imaginary_value কে এক্সেস(access) করতে চাচ্ছেন তাহলে নিচের স্ট্রাকচার মেম্বার ব্যবহৃত হবেঃ
number2.comp.imaginary_value
ফাংশনের মধ্যে দিয়ে স্ট্রাকচার অতিক্রম করানোর দুটি পদ্ধিত রয়েছেঃ
স্ট্রাকচার ও ফাংশন অধ্যায়ে এ সম্বন্ধে বিস্তারিত আলোচনা করা হয়েছে।
এই অধ্যায়ে আপনি স্ট্রাকচার(structure) এবং পয়েন্টার(pointer) সম্পর্কিত কিছু প্রাসঙ্গিক উদাহরণ দেখবেন যা আপনাকে স্ট্রাকচার থকে পয়েন্টার(pointer) ব্যবহার করে ডেটা এক্সেস(data access) করার জন্য সাহায্য করবে।
পয়েন্টার(pointer) ব্যবহার করে স্ট্রাকচার(structure) তৈরি এবং এক্সেস উভয়ই করা যেতে পারে। নিম্নের ন্যায় স্ট্রাকচার টাইপের পয়েন্টার ভ্যারিয়েবল তৈরি করা যেতে পারেঃ
struct name {
member1;
member2;
.
.
};
int main()
{
struct name *ptr;
}
উপরের প্রোগ্রামে struct name
টাইপের একটি পয়েন্টার ভ্যারিয়েবল(*ptr) তৈরি হয়েছে।
পয়েন্টারের মাধ্যমে স্ট্রাকচারের মেম্বার(member) কে দুইভাবে এক্সেস করা যায়ঃ
- পয়েন্টারকে অন্য এড্রেসে রেফার করে মেমোরি এক্সেস করা
- ডাইনামিক মেমোরি এলোকেশন(dynamic memory allocation) ব্যবহার করে।
পয়েন্টারের মাধ্যমে স্ট্রাকচারের মেম্বারকে এক্সেস করার জন্য সি প্রোগ্রামঃ
#include <stdio.h>
typedef struct person
{
int age;
float weight;
};
int main()
{
struct person *personPtr, person1;
personPtr = &person1; //person1 এর মেমোরি এড্রেসে পয়েন্টারকে রেফার করা
printf("Enter integer: ");
scanf("%d",&(*personPtr).age);
printf("Enter number: ");
scanf("%f",&(*personPtr).weight);
printf("Displaying: ");
printf("%d%f",(*personPtr).age,(*personPtr).weight);
return 0;
}
উপরের উদাহরণে struct person
টাইপের পয়েন্টার ভ্যারিয়েবলকে person1 ভ্যারিয়েবলের এড্রেসে রেফার(refer) করা হয়েছে। তাই শুধুমাত্র পয়েন্টারের মাধ্যমে স্ট্রাকচার মেম্বারকে এক্সেস(access) করা যায়।
স্ট্রাকচার এর পয়েন্টার মেম্বারকে এক্সেস করার জন্য ->(Arrow) অপারেটর ব্যবহার করা হয়।
.(Dot)
অপারেটর ব্যবহার করেও স্ট্রাকচার এর পয়েন্টার মেম্বারকে এক্সেস করতে পারেন।
(*personPtr).age এবং personPtr->age একই রকম
(*personPtr).weight এবং personPtr->weight একই রকম
পয়েন্টারের মাধ্যমে স্ট্রাকচারের মেম্বারকে এক্সেস করার জন্য malloc() ফাংশন ব্যবহার করে ডাইনামিকভাবে মেমোরি বরাদ্দ করা যেতে পারে।
ptr = (cast-type*) malloc(byte-size)
malloc() ফাংশন ব্যবহার করে পয়েন্টারের মাধ্যমে স্ট্রাকচারের মেম্বারকে এক্সেস করার জন্য সি প্রোগ্রাম
kt_satt_skill_example_id=536
এই অধ্যায়ে আপনি স্ট্রাকচার(structure)-কে আর্গুমেন্ট হিসাবে ফাংশনের মধ্য দিয়ে অতিক্রম করানো সম্পর্কিত কিছু উদাহরণ দেখবেন এবং আপনার প্রোগ্রামে ব্যবহার করা শিখবেন।
সি প্রোগ্রামে structure কে function এর মধ্য দিয়ে দুইভাবে অতিক্রম(pass) করানো যায়ঃ
- ভ্যালু হিসাবে অতিক্রম করানো(Passing by value)
- রেফারেন্স হিসাবে অতিক্রম করানো(Passing by reference)
সাধারণ ভ্যারিয়েবলের মত স্ট্রাকচারকেও(structure) ফাংশনের মধ্য দিয়ে আর্গুমেন্ট(argument) হিসাবে অতিক্রম করানো যেতে পারে।
যদি স্ট্রাকচারকে ফাংশনের মধ্য দিয়ে ভ্যালু হিসাবে অতিক্রম(pass) করানো হয় তাহলে ফাংশন ডেফিনিশন(definition) এর মধ্যে স্ট্রাকচার ভ্যারিয়েবলের পরিবর্তন ঘটলেও অরিজিনাল স্ট্রাকচার ভ্যারিয়েবলে কোনো ধরনের প্রভাব ফেলবে না।
উদাহরনঃ student নামের একটি স্ট্রাকচার তৈরির জন্য সি প্রোগ্রাম, যাতে স্ট্রাকচার এর মেম্বার হিসাবে স্টুডেন্টের নাম ও রোল নাম্বার রয়েছে এবং তথ্য প্রদর্শনীর জন্য display() ফাংশন ব্যবহার করা হয়েছে।
kt_satt_skill_example_id=550
স্ট্রাকচারকে রেফারেন্স হিসাবে ফাংশনের মধ্য দিয়ে অতিক্রম করানো হলে স্ট্রাকচার ভ্যারিয়েবলের মেমোরি এড্রেস ফাংশনের মধ্য দিয়ে অতিক্রম করে।
যদি স্ট্রাকচারকে ফাংশনের মধ্য দিয়ে রেফারেন্স(reference) হিসাবে অতিক্রম(pass) করানো হয় তাহলে ফাংশন ডেফিনিশন(definition) এর মধ্যে স্ট্রাকচার ভ্যারিয়েবলের পরিবর্তন ঘটলে অরিজিনাল স্ট্রাকচার ভ্যারিয়েবলেও প্রভাব পড়বে।
ফুট-ইঞ্চিতে দুটি দূরত্বের যোগ এবং রিটার্ন স্টেটমেন্ট ব্যবহার না করে ফলাফল প্রদর্শনীর জন্য সি প্রোগ্রাম।
kt_satt_skill_example_id=554
- উপরের প্রোগ্রামে dist1 এবং dist2 স্ট্রাকচার ভ্যারিয়েবলকে
add
ফাংশনের মধ্য দিয়ে ভ্যালু(value) হিসাবে অতিক্রম(pass) করানো হয়েছে। কারণ dist1 এবং dist2 কে main() ফাংশনের মধ্যে প্রদর্শনীর প্রয়োজন নাই।- কিন্তু dist3 কে রেফারেন্স হিসাবে অতিক্রম করানো হয়েছে। অর্থাৎ dist3 এর এড্রেসকে(
(&dist3)
) ফাংশনের আর্গুমেন্ট হিসাবে অতিক্রম করানো হয়েছে।- এই কারণে main ফাংশন থেকে
add
ফাংশনকে কল(call) করলেadd
ফাংশনের মধ্যে d3(স্ট্রাকচার পয়েন্টার ভ্যারিয়েবল) dist3 (স্ট্রাকচার ভ্যারিয়েবল) এর এড্রেসকে নির্দেশ(point) করে। তাই d3 ভ্যারিয়েবলে কোনো পরিবর্তন আনলে main ফাংশনের dist3 ভ্যারিয়বলে তা দেখা দেয়।- ফলে সঠিক যোগফল আউটপুটে প্রদর্শীত হয়।
এই অধ্যায়ে আপনি সি প্রোগ্রামিং এ ইউনিয়ন সম্মন্ধে জানবেন। আরোও নির্দিষ্ট করে বললেঃ ইউনিয়ন তৈরি করা, এক্সেস করা এবং ইউনিয়ন(Union) ও স্ট্রাকচার(structure) এর মধ্যে পার্থক্য তৈরি করতে শিখবেন।
সি প্রোগ্রামিং এ ইউনিয়ন সম্পূর্নরূপে স্ট্রাকচার এর মতই। স্ট্রাকচারের ন্যায় ইউনিয়নও ডিরাইভ(derived) ডেটা টাইপ।
সি প্রোগ্রামিং এ ইউনিয়ন(Union) ডিফাইন করা খুবই সহজ এবং হুবহু স্ট্রাকচারের মতই। struct কীওয়ার্ডের পরিবর্তে শুধুমাত্র union কীওয়ার্ড ব্যবহার করতে হয়।
union car
{
char name[50];
int price;
};
স্ট্রাকচার ভ্যারিয়েবল যে পদ্ধতিতে তৈরি করা হয় ঠিক একই পদ্ধতিতে ইউনিয়ন ভ্যারিয়েবলও তৈরি করা হয়।
union car
{
char name[50];
int price;
} car1, car2, *car3;
অথবা
union car
{
char name[50];
int price;
};
int main()
{
union car car1, car2, *car3;
return 0;
}
উভয় ক্ষেত্রেই union car
ডেটা টাইপের car1 ও car2 ইউনিয়ন ভ্যারিয়েবল এবং car3 ইউনিয়ন পয়েন্টার ভ্যারিয়েবল তৈরি হয়েছে।
স্ট্রাকচারের মেম্বারকে যে পদ্ধতিতে এক্সেস করা হয় ঠিক একই পদ্ধতিতে ইউনিয়ন এর মেম্বারকেও এক্সেস করা হয়।
উপরের উদাহরণে আপনি যদি ইউনিয়ন ভ্যারিয়েবল car এর মেম্বার price কে এক্সেস করতে চান তাহলে নিচের পদ্ধতি অনুসরণ করুনঃ
car1.price
একইভাবে আপনি যদি ইউনিয়ন পয়েন্টার ভ্যারিয়েবল- car3 এর মেম্বার price কে এক্সেস করতে চান তাহলে নিচের পদ্ধতি অনুসরণ করুনঃ
(*car3).price
অথবা;
*car3->price
ইউনিয়ন এবং স্ট্রাকচার অনেক ক্ষেত্রে একই রকম হওয়া সত্ত্বেও এদের মধ্যে পার্থক্য বুঝা খুবই গুরুত্বপূর্ণ।
নিচের উদাহরণের সাহায্যে চলুন ইউনিয়ন এবং স্ট্রাকচারের প্রাথমিক পার্থক্য বুঝার চেষ্টা করিঃ
kt_satt_skill_example_id=565
উপরের উদাহরণে ইউনিয়ন এবং স্ট্রাকচারের মধ্যে মেমোরি বরাদ্দ বিষয়ক পার্থক্য দেখানো হয়েছে।
স্ট্রাকচার ভ্যারিয়েবলের ক্ষেত্রে সকল মেম্বারের যোগফলের সমান মেমোরি বরাদ্দ রাখতে হয়।
অপরপক্ষে ইউনিয়ন ভ্যারিয়েবলের ক্ষেত্রে ইউনিয়নের সর্বোচ্চ এলিমেন্টের সমান মেমোরির প্রয়োজন হয়। ।
স্টারকচারঃ একই সঙ্গে ইহার সকল মেম্বারকে এক্সেস করা যায়।
ইউনিয়নঃ একই সঙ্গে ইহার একটিমাত্র মেম্বারকে এক্সেস করা যায় এবং অন্যান্য সকল মেম্বার গার্বেজ(garbage) ভ্যালু ধারণ করে।
ex-2
বিঃদ্রঃ নামের জন্য আপনি বিভিন্ন গার্বেজ ভ্যালু পেতে পারেন।
উপরের প্রোগ্রামে job1.name
এর মধ্যে প্রথমে Tamim স্টোর করা হয় এবং job1 এর অন্যান্য সকল মেম্বার যেমন salary, workerNo গার্বেজ ভ্যালু ধারণ করবে।
কিন্তু ইউজার যখন salary এর ভ্যালু প্রবেশ করায় তখন job1.salary
এর মধ্যে 1234.23 স্টোর হয় এবং অন্যান্য মেম্বার যেমন-name, workerNo গার্বেজ ভ্যালু ধারণ করবে।
এইভাবে আউটপুটে salary সঠিকভাবে প্রিন্ট হবে কিন্তু name কিছু র্যান্ডম(random) স্ট্রিং ডিসপ্লে করে।
সি প্রোগ্রামিং এ স্ট্রাকচারকে যে পদ্ধতিতে ফাংশনের মধ্য দিয়ে অতিক্রম করানো হয়, একই পদ্ধতিতে ইউনিয়নকেও অতিক্রম করানো হয়।
আরোও জানতে ভিজিট করুনঃ সি প্রোগ্রামিং এ স্ট্রাকচারকে কিভাবে অতিক্রম করানো হয়?
ফাইল ইনপুট/আউটপুট(I/O) পরিচালনার জন্য সি প্রোগ্রমিং এ অনেক ধরণের ফাংশন রয়েছে। এই অধ্যায়ে আপনি fprintf(), fscanf(), fread(), fwrite(), fseek এবং আরো অনেক ধরেনের ফাংশন ব্যবহার করে সি এর স্টান্ডার্ড ইনপুট/আউটপুট পরিচালনা করা শিখবেন।
সি প্রোগ্রামিং এ ফাইল হলো আপনার কম্পিউটারের ফিজিক্যাল ডিস্কের মধ্যে একটি স্থান যেখানে তথ্য জমা হয়।
- যখন প্রোগ্রাম এর সমাপ্তি ঘটে তখন সমস্ত তথ্য(Data) হারিয়ে যায়। কিন্তু ফাইলের মধ্যে সংরক্ষণ করলে প্রোগ্রাম শেষ হয়ে গেলেও তথ্য সংরক্ষিত অবস্থায় থাকে।
- আপনাকে যদি অনেক বেশী ডাটা ইনপুট দিতে হয় তাহলে সমস্ত ডাটা ইনপুট দেওয়ার জন্য অনেক সময় লেগে যাবে।
যাইহোক সমস্ত তথ্য(data) সম্বলিত একটি ফাইল যদি আপনার কাছে থাকে তাহলে আপনি সি এর কিছু কমান্ডের সাহায্যে খুব সহজেই সেই কন্টেন্টসমূহ এক্সেস করতে পারেন।- অত্যন্ত সহজভাবে আপনি আপনার ডাটা এক কম্পিউটার থেকে অন্য কম্পিউটারে স্থানান্তর করতে পারেন।
আপনি যখন ফাইল নিয়ে ডিল(deal) করবেন তখন দুই ধরণের ফাইল দেখতে পাবেন যা আপনার জানা আবশ্যকঃ
- টেক্সট ফাইল(Text file)
- বাইনারি ফাইল(Binary file)
টেক্সট ফাইল | বাইনারি ফাইল |
---|---|
.txt এক্সটেনশন দিয়ে সাধারণ টেক্সট ফাইল। | .bin এক্সটেনশন দিয়ে আপনার কম্পিউটারে সংরক্ষিত থাকে। |
ডেটা টেক্সট ফরম্যাটে(অ, আ, ক, খ )সংরক্ষণ করে, ফলে সম্পাদনা অনেক সহজ। | ডেটা টেক্সট এর পরিবর্তে বাইনারি ফরম্যাটে(০ এবং ১) সংরক্ষণ করে, ফলে সম্পাদনা কঠিন। |
সহজে পাঠযোগ্য। | পাঠযোগ্য নহে। |
কম পরিশ্রমে রক্ষণাবেক্ষণ করা যায়। | রক্ষণাবেক্ষণ অনেক কঠিন। |
মেমোরিতে অনেক স্পেস দখল করে। | কম স্পেসে অনেক তথ্য ধারণ করতে পারে। |
নিম্নমানের সিকিউরিটি সরবরাহ করে। | সর্বোচ্চ মানের সিকিউরিটি সরবরাহ করে। |
সি প্রোগ্রামে টেক্সট অথবা বাইনারি ফাইল অপারেশনের জন্য চারটি মুখ্য অপারেশন রয়েছেঃ
নতুন ফাইল তৈরি করা।
বিদ্যমান ফাইল খুলা।
ফাইল বন্ধ করা।
বিদ্যমান ফাইল থেকে তথ্য পাঠ এবং তথ্য যোগ করা।
আপনি যখন ফাইল নিয়ে কাজ করবেন তখন file
টাইপের একটি পয়েন্টার ডিক্লেয়ার করতে হবে। প্রোগ্রাম এবং ফাইলের মধ্যে যোগাযোগ সৃষ্টির জন্য এই ডিক্লেয়ারেশনের প্রয়োজন।
FILE *fptr;
"stdio.h" হেডার ফাইলের আওতাধীন fopen() লাইব্রেরী ফাংশন এর মাধ্যমে ফাইল খুলার কাজটি করা হয়।
ptr = fopen("fileopen","mode")
উদাহরণস্বরূপঃ
fopen("E:\\cprogramming\\newFile.txt","w");
fopen("E:\\cprogramming\\oldFile.bin","rb");
- ধরুন
E:\cprogramming
লোকেশনে প্রথম টেক্সট ফাইলnewFile.txt
ফাইলটি পূর্বে থেকে বিদ্যমান নাই। ফাইল মোড(mode) "w" অনুসারে প্রথম ফাংশনটিnewFile.txt
নামের একটি নতুন ফাইল তৈরি করে।
ফাইলের <W> মোড আপনাকে নতুন ফাইল তৈরি এবং সম্পাদনা/ওভার রাইট করার সম্মতি দেয়।- আবার ধরুন E:\cprogramming লোকেশনে দ্বিতীয় বাইনারি ফাইল
oldFile.bin
ফাইলটি পূর্বে থেকেই বিদ্যমান আছে। দ্বিতীয় ফাংশনটি বিদ্যমান ফাইলকে বাইনারি মোডে( 'rb') পাঠ করার জন্য ওপেন করে।
'r' মোডে কোনো ফাইল ওপেন করলে আপনি শুধু ফাইলটি পাঠ(read) করার অনুমতি পাবেন, কোনো কিছু পরিবর্তন/লেখা-লেখি করতে পারবেন না।
ফাইল মোড | বর্ণনা | ফাইল বিদ্যমান না থাকলে |
---|---|---|
r | পাঠ করার জন্য ফাইল ওপেন করে। | ফাইল বিদ্যমান না থাকলে fopen() ফাংশন NULL রিটার্ন করে। |
rb | বাইনারি মোডে পাঠ করার জন্য ফাইল ওপেন করে। | ফাইল বিদ্যমান না থাকলে fopen() ফাংশন NULL রিটার্ন করে। |
w | লেখার জন্য ফাইল ওপেন করে। | ফাইল বিদ্যমান থাকলে এর কন্টেন্ট-সমূহ অভার-রাইট হয়ে যাবে। যদি ফাইল বিদ্যমান না থাকে তাহলে নতুন ফাইল তৈরি হবে। |
wb | বাইনারি মোডে লেখার জন্য ফাইল ওপেন করে। | ফাইল বিদ্যমান থাকলে এর কন্টেন্ট-সমূহ অভার-রাইট হয়ে যাবে। যদি ফাইল বিদ্যমান না থাকে তাহলে নতুন ফাইল তৈরি হবে। |
a | ডেটা সংযোজন(append) করার জন্য ফাইল ওপেন করে। সুতরাং ফাইলের শেষে তথ্য যোগ করা যায়। | যদি ফাইল বিদ্যমান না থাকে তাহলে নতুন ফাইল তৈরি হবে। |
ab | বাইনারি মোডে ডেটা সংযোজন(append) করার জন্য ফাইল ওপেন করে। সুতরাং ফাইলের শেষে তথ্য যোগ করা যায়। | যদি ফাইল বিদ্যমান না থাকে তাহলে নতুন ফাইল তৈরি হবে। |
r+ | পাঠ এবং লেখা উভয়ের জন্য ফাইল ওপেন করে। | ফাইল বিদ্যমান না থাকলে fopen() ফাংশন NULL রিটার্ন করে। |
rb+ | বাইনারি মোডে পাঠ এবং লেখা উভয়ের জন্য ফাইল ওপেন করে। | ফাইল বিদ্যমান না থাকলে fopen() ফাংশন NULL রিটার্ন করে। |
w+ | পাঠ এবং লেখা উভয়ের জন্য ওপেন করে। | ফাইল বিদ্যমান থাকলে এর কন্টেন্ট-সমূহ অভার-রাইট হয়ে যাবে। যদি ফাইল বিদ্যমান না থাকে তাহলে নতুন ফাইল তৈরি হবে। |
wb+ | বাইনারি মোডে পাঠ এবং লেখা উভয়ের জন্য ফাইল ওপেন করে। | ফাইল বিদ্যমান থাকলে এর কন্টেন্ট-সমূহ অভার-রাইট হয়ে যাবে। যদি বিদ্যমান না থাকে তাহলে নতুন ফাইল তৈরি হবে। |
a+ | ডেটা পাঠ এবং সংযোজন(append) উভয়ের জন্য ফাইল ওপেন করে। | যদি ফাইল বিদ্যমান না থাকে তাহলে নতুন ফাইল তৈরি হবে। |
ab+ | বাইনারি মোডে ডেটা পাঠ এবং সংযোজন(append) উভয়ের জন্য ফাইল ওপেন করে। | যদি ফাইল বিদ্যমান না থাকে তাহলে নতুন ফাইল তৈরি হবে। |
পড়া/লেখার পরে টেক্সট এবং বাইনারি উভয় ফাইলকেই বন্ধ করা উচিৎ।
fclose() লাইব্রেরী ফাংশন ব্যবহার করে ফাইল বন্ধের কাজটি করা হয়।
fclose(fptr); //এখানে fptr হলো যে ফাইলকে বন্ধ করবো সেই ফাইলের ফাইল পয়েন্টার
টেক্সট ফাইলে পড়া এবং লেখার জন্য আমরা যথাক্রমে fprintf()
এবং fscanf()
লাইব্রেরী ফাংশন ব্যবহার করি।
fprintf()
এবং fscanf()
ফাংশনটি দুটি printf()
এবং scanf()
এর ফাইল ভার্সন। শুধুমাত্র পার্থক্য হলো fprint এবং fscanf ফাংশন দুটি ফাইল এর অন্তত একটি পয়েন্টার আশা করে।
উদাহরনঃ fprintf() ফাংশন ব্যবহার করে ফাইলে লেখা
#include <stdio.h>
int main()
{
int num;
FILE *fptr;
fptr = fopen("C:\\program.txt","w");
if(fptr == NULL)
{
printf("Error!");
exit(1);
}
printf("Enter num: ");
scanf("%d",&num);
fprintf(fptr,"%d",num);
fclose(fptr);
return 0;
}
এই প্রোগ্রামটি ইউজার থেকে নম্বর গ্রহণ করে program.txt
ফাইলে জমা করে।
এই প্রোগ্রামটি কম্পাইল এবং রান করানোর পরে আপনার কম্পিউটারের সি ড্রাইভে program.txt নামের একটি ফাইল দেখতে পাবেন। আপনি যখন ফাইলটি ওপেন করবেন তখন আপনার প্রবেশ করানো পূর্ণসংখ্যাটি(integer number) দেখতে পাবেন।
উদাহরনঃ fscanf() লাইব্রেরী ফাংশন ব্যবহার করে ফাইল পড়া।
#include <stdio.h>
int main()
{
int num;
FILE *fptr;
if ((fptr = fopen("C:\\program.txt","r")) == NULL){
printf("Error! opening file");
// ফাইল পয়েন্টার NULL রিটার্ন করলে প্রোগ্রাম থেকে বের হয়ে যাবে।
exit(1);
}
fscanf(fptr,"%d",&num);
printf("Value of n=%d",num);
fclose(fptr);
return 0;
}
এই প্রোগ্রামটি program.txt
ফাইলে বিদ্যমান পূর্ণসংখ্যা পড়ে এবং স্ক্রিনে প্রিন্ট করে।
প্রথম উদাহরণে যদি সফলভাবে ফাইল তৈরি হয় তাহলে এই প্রোগ্রাম রান করিয়ে আপনি প্রবেশ করানো পূর্ণসংখ্যাটি পাবেন।
অন্যান্য ফাংশন যেমন- fgetchar()
, fputc()
ইত্যাদি একইভাবে ব্যবহার করতে পারেন।
আপনার কম্পিউটার ডিস্কে বাইনারি ফাইল লেখা এবং সেখান থেকে বাইনারি ফাইল পড়ার জন্য যথাক্রমে fwrite()
এবং fread()
লাইব্রেরী ফাংশন ব্যবহার করা হয়।
বাইনারি ফাইলে কোনো কিছু লেখার জন্য আপনাকে fwrite() ফাংশনটি ব্যবহার করতে হবে। এই ফাংশনটি চারটি আরগুমেন্ট গ্রহণ করে। যথাঃ তথ্য(data) লিপিবদ্ধ করার জন্য কম্পিউটার ডিস্কের ঠিকানা, ডিস্কে লিপিবদ্ধ তথ্যের পরিমাণ(Size), তথ্যের সংখ্যা এবং তথ্য জমা হবে এমন স্থানের ফাইলের পয়েন্টার।
fwrite(data_address, data_size, data_numbers, pointer_to_file);
উদাহরনঃ fwrite() ব্যবহার করে বাইনারি ফাইল লেখা।
#include <stdio.h>
struct threeNum
{
int n1, n2, n3;
};
int main()
{
int n;
struct threeNum num;
FILE *fptr;
if ((fptr = fopen("C:\\program.bin","wb")) == NULL){
printf("Error! opening file");
// Program exits if the file pointer returns NULL.
exit(1);
}
for(n = 1; n < 5; ++n)
{
num.n1 = n;
num.n2 = 5n;
num.n3 = 5n + 1;
fwrite(&num, sizeof(struct threeNum), 1, *fptr);
}
fclose(fptr);
return 0;
}
এই প্রোগ্রামের মাধ্যমে আপনি সি ড্রাইভে program.bin
নামের একটি নতুন ফাইল তৈরি করতে পারেন।
আমরা তিনটি নম্বর - n1, n2 এবং n3 সহ একটি স্ট্রাকচার ভ্যারিয়েবল threeNum
ডিক্লেয়ার করেছি এবং main ফাংশনে ইহাকে num হিসাবে ডিফাইন করেছি।
এখন for লুপের ভিতরে fwrite() ফাংশন ব্যবহার করে ফাইলে ভ্যালু জমা(store) করতে পারি।
প্রথম প্যারামিটারটি num ভ্যারিয়েবলের এড্রেস গ্রহণ করে এবং দ্বিতীয় প্যারামিটারটি threeNum
স্ট্রাকচারের সাইজ গ্রহণ করে।
যেহেতু আমরা num এর একটিমাত্র instance ইনসার্ট করেছি, সুতরাং তৃতীয় প্যারমিটারের ভ্যালু 1
। শেষ প্যারামিটার *fptr
ডেটা স্টোরের ফাইলকে নির্দেশ করে।
সবশেষে আমরা ফাইলকে বন্ধ করি।
উপরের fwrite()
ফাংশনের ন্যায় fread()
ফাংশনও চারটি আর্গুমেন্ট গ্রহণ করে।
fread(data_address, data_size, data_numbers, pointer_to_file);
উদাহরনঃ fread() ফাংশন ব্যবহার করে বাইনারি ফাইল থেকে তথ্য পড়াঃ
#include <stdio.h>
struct threeNum
{
int n1, n2, n3;
};
int main()
{
int n;
struct threeNum num;
FILE *fptr;
if ((fptr = fopen("C:\\program.bin","rb")) == NULL){
printf("Error! opening file");
// ফাইল পয়েন্টার NULL রিটার্ন করলে প্রোগ্রাম থেকে বের হয়ে যাবে।
exit(1);
}
for(n = 1; n < 5; ++n)
{
fread(&num, sizeof(struct threeNum), 1, *fptr);
printf("n1: %d\tn2: %d\tn3: %d", num.n1, num.n2, num.n3);
}
fclose(fptr);
return 0;
}
এই প্রোগ্রামটির মাধ্যমে আপনি একই ফাইল program.bin
কে পড়তে পারবেন এবং ফাইলের রেকর্ড-সমূহকে একের পরে এক আবৃতি(loop) করতে পারবেন।
সহজ অর্থে, আপনি *fptr ফাইল পয়েন্টার দ্বারা num স্ট্রাকচারে নির্দেশিতে threeNum
সাইজের একটি threeNum
রেকর্ডকে পড়তে পারবেন।
উপরের উদাহরণে আপনি যা কিছু রেকর্ড করবেন ঐ একই রেকর্ড ফিরে পাবেন।
ফাইলের মধ্যে যদি বহু তথ্য রেকর্ড করা থাকে এবং একটি নির্দিষ্ট পজিশনে ঐ রেকর্ডকে এক্সেস করার প্রয়োজন হয় তাহলে সমগ্র রেকর্ড লুপ(loop) করার প্রয়োজন হবে।
ইহা প্রচুর পরিমাণ মেমোরি এবং অপারেশনের সময় অপচয় করবে। এই সমস্যা থেকে কাটিয়ে উঠার জন্য আপনি fseek() ফাংশন ব্যবহার করতে পারেন যার মাধ্যমে সময় ও শ্রম উভয়ই সাশ্রয় হবে।
fseek() ফাংশন ফাইলের মধ্য থেকে চাওয়া রেকর্ডে অতি সহজেই কার্সর নিয়ে যায়।
fseek(FILE * stream, long int offset, int whence)
এখানে ফাংশনের প্রথম প্যারামিটার stream হলো ফাইলের পয়েন্টার, দ্বিতীয় প্যারামিটারটি রেকর্ডের পজিশন যাকে খুঁজে বের করতে হবে, এবং তৃতীয় প্যারামিটারটি হলো লোকেশন যেখান থেকে offset শুরু করবে।
Whence | বর্ণনা |
---|---|
SEKK_SET | ফাইলের একেবারে শুরু থেকে মুদ্রণ(offset) শুরু হয়। |
SEKK_END | ফাইলের একেবারে শেষ থেকে মুদ্রণ(offset) শুরু হয়। |
SEKK_CUR | ফাইল কার্সরের চলমান লোকেশন থেকে মুদ্রণ শুরু হয়। |
#include <stdio.h>
struct threeNum
{
int n1, n2, n3;
};
int main()
{
int n;
struct threeNum num;
FILE *fptr;
if ((fptr = fopen("C:\\program.bin","rb")) == NULL){
printf("Error! opening file");
// ফাইল পয়েন্টার NULL রিটার্ন করলে প্রোগ্রাম এখান থেকে বের হয়ে যায়।
exit(1);
}
// কার্সর একবারে ফাইলের শেষে যায়।
fseek(fptr, sizeof(struct threeNum), SEEK_END);
for(n = 1; n < 5; ++n)
{
fread(&num, sizeof(struct threeNum), 1, *fptr);
printf("n1: %d\tn2: %d\tn3: %d", num.n1, num.n2, num.n3);
}
fclose(fptr);
return 0;
}
এই প্রোগ্রামটি উল্টা দিক হতে(অর্থাৎ শেষ থেকে প্রথমে)program.bin
বাইনারি ফাইল থেকে রেকর্ড পড়া শুরু করে এবং সঙ্গে সঙ্গে প্রিন্টও করে।
common.read_more